//! Template engine for variable and function resolution
//! 
//! This module provides template processing capabilities with support for
//! variable substitution and function calls, compatible with HttpRunner syntax.

use crate::dsl::variables::VariableManager;
use crate::dsl::functions::{FunctionRegistry, FunctionCall};
use crate::error::{Error, Result};
use regex::Regex;
use serde_json::Value;
use std::collections::HashMap;

/// Template engine for processing variable and function templates
pub struct TemplateEngine {
    /// Function registry for template functions
    function_registry: FunctionRegistry,
    
    /// Variable pattern regex for ${variable} syntax
    variable_pattern: Regex,
    
    /// Function pattern regex for ${function(args)} syntax
    function_pattern: Regex,
    
    /// Environment variable pattern for ${ENV(VAR)} syntax
    env_pattern: Regex,
}

/// Template processing context
#[derive(Debug, Clone)]
pub struct TemplateContext {
    /// Current processing depth (to prevent infinite recursion)
    pub depth: usize,
    
    /// Maximum processing depth
    pub max_depth: usize,
    
    /// Processing statistics
    pub stats: TemplateStats,
}

/// Template processing statistics
#[derive(Debug, Clone, Default)]
pub struct TemplateStats {
    /// Number of variables resolved
    pub variables_resolved: usize,
    
    /// Number of functions called
    pub functions_called: usize,
    
    /// Number of environment variables accessed
    pub env_vars_accessed: usize,
    
    /// Processing warnings
    pub warnings: Vec<String>,
}

impl TemplateEngine {
    /// Create a new template engine
    pub fn new() -> Self {
        Self {
            function_registry: FunctionRegistry::new(),
            variable_pattern: Regex::new(r"\$\{([^}]+)\}").unwrap(),
            function_pattern: Regex::new(r"\$\{([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)\}").unwrap(),
            env_pattern: Regex::new(r"\$\{ENV\(([^)]+)\)\}").unwrap(),
        }
    }
    
    /// Create a new template engine with custom function registry
    pub fn with_functions(function_registry: FunctionRegistry) -> Self {
        Self {
            function_registry,
            variable_pattern: Regex::new(r"\$\{([^}]+)\}").unwrap(),
            function_pattern: Regex::new(r"\$\{([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)\}").unwrap(),
            env_pattern: Regex::new(r"\$\{ENV\(([^)]+)\)\}").unwrap(),
        }
    }
    
    /// Resolve a template string with variables and functions
    pub fn resolve_string(&mut self, template: &str, variable_manager: &mut VariableManager) -> Result<String> {
        let mut context = TemplateContext::new();
        self.resolve_string_with_context(template, variable_manager, &mut context)
    }

    /// Resolve a template string with context
    pub fn resolve_string_with_context(
        &mut self,
        template: &str,
        variable_manager: &mut VariableManager,
        context: &mut TemplateContext,
    ) -> Result<String> {
        if context.depth >= context.max_depth {
            return Err(Error::Internal("Template resolution depth exceeded".to_string()));
        }

        context.depth += 1;
        let mut result = template.to_string();

        // First pass: resolve environment variables
        result = self.resolve_env_variables(&result, context)?;

        // Second pass: resolve function calls
        result = self.resolve_functions(&result, variable_manager, context)?;

        // Third pass: resolve regular variables
        result = self.resolve_variables(&result, variable_manager, context)?;

        context.depth -= 1;
        Ok(result)
    }
    
    /// Resolve a JSON value (recursively process strings)
    pub fn resolve_value(&mut self, value: &Value, variable_manager: &mut VariableManager) -> Result<Value> {
        let mut context = TemplateContext::new();
        self.resolve_value_with_context(value, variable_manager, &mut context)
    }

    /// Resolve a JSON value with context
    pub fn resolve_value_with_context(
        &mut self,
        value: &Value,
        variable_manager: &mut VariableManager,
        context: &mut TemplateContext,
    ) -> Result<Value> {
        match value {
            Value::String(s) => {
                let resolved = self.resolve_string_with_context(s, variable_manager, context)?;
                // Try to parse as JSON if it looks like a JSON value
                if resolved.starts_with('{') || resolved.starts_with('[') || resolved.starts_with('"') {
                    Ok(serde_json::from_str(&resolved).unwrap_or(Value::String(resolved)))
                } else if let Ok(num) = resolved.parse::<i64>() {
                    Ok(Value::Number(num.into()))
                } else if let Ok(num) = resolved.parse::<f64>() {
                    Ok(Value::Number(serde_json::Number::from_f64(num).unwrap_or(0.into())))
                } else if let Ok(bool_val) = resolved.parse::<bool>() {
                    Ok(Value::Bool(bool_val))
                } else {
                    Ok(Value::String(resolved))
                }
            }
            Value::Array(arr) => {
                let mut resolved_arr = Vec::new();
                for item in arr {
                    resolved_arr.push(self.resolve_value_with_context(item, variable_manager, context)?);
                }
                Ok(Value::Array(resolved_arr))
            }
            Value::Object(obj) => {
                let mut resolved_obj = serde_json::Map::new();
                for (key, val) in obj {
                    let resolved_key = self.resolve_string_with_context(key, variable_manager, context)?;
                    let resolved_val = self.resolve_value_with_context(val, variable_manager, context)?;
                    resolved_obj.insert(resolved_key, resolved_val);
                }
                Ok(Value::Object(resolved_obj))
            }
            _ => Ok(value.clone()),
        }
    }
    
    /// Resolve environment variables in template
    fn resolve_env_variables(&self, template: &str, context: &mut TemplateContext) -> Result<String> {
        let mut result = template.to_string();
        
        for cap in self.env_pattern.captures_iter(template) {
            let full_match = &cap[0];
            let var_name = &cap[1];
            
            if let Ok(env_value) = std::env::var(var_name) {
                result = result.replace(full_match, &env_value);
                context.stats.env_vars_accessed += 1;
            } else {
                context.stats.warnings.push(format!("Environment variable '{}' not found", var_name));
                // Keep the original template if env var not found
            }
        }
        
        Ok(result)
    }
    
    /// Resolve function calls in template
    fn resolve_functions(
        &mut self,
        template: &str,
        variable_manager: &mut VariableManager,
        context: &mut TemplateContext,
    ) -> Result<String> {
        let mut result = template.to_string();

        // Collect all captures first to avoid borrowing conflicts
        let captures: Vec<_> = self.function_pattern.captures_iter(template).collect();

        for cap in captures {
            let full_match = &cap[0];
            let function_name = &cap[1];
            let args_str = &cap[2];

            // Parse function arguments
            let args = self.parse_function_args(args_str, variable_manager, context)?;

            // Create function call
            let function_call = FunctionCall {
                name: function_name.to_string(),
                args,
            };

            // Execute function
            if let Ok(function_result) = self.function_registry.call(&function_call) {
                let result_str = match function_result {
                    Value::String(s) => s,
                    Value::Number(n) => {
                        if n.is_f64() {
                            let f = n.as_f64().unwrap();
                            if f.fract() == 0.0 {
                                // If it's a whole number, format as integer
                                format!("{}", f as i64)
                            } else {
                                f.to_string()
                            }
                        } else {
                            n.to_string()
                        }
                    },
                    Value::Bool(b) => b.to_string(),
                    _ => serde_json::to_string(&function_result).unwrap_or_default(),
                };
                result = result.replace(full_match, &result_str);
                context.stats.functions_called += 1;
            } else {
                context.stats.warnings.push(format!("Function '{}' not found or failed", function_name));
            }
        }

        Ok(result)
    }
    
    /// Resolve variables in template
    fn resolve_variables(
        &self,
        template: &str,
        variable_manager: &mut VariableManager,
        context: &mut TemplateContext,
    ) -> Result<String> {
        let mut result = template.to_string();

        for cap in self.variable_pattern.captures_iter(template) {
            let full_match = &cap[0];
            let var_name = &cap[1];

            // Skip if it's a function call or env var (already processed)
            if var_name.contains('(') || var_name.starts_with("ENV(") {
                continue;
            }

            if let Some(var_value) = variable_manager.get_variable(var_name) {
                let value_str = match var_value {
                    Value::String(s) => s.clone(),
                    Value::Number(n) => n.to_string(),
                    Value::Bool(b) => b.to_string(),
                    _ => serde_json::to_string(var_value).unwrap_or_default(),
                };
                result = result.replace(full_match, &value_str);
                context.stats.variables_resolved += 1;
            } else {
                context.stats.warnings.push(format!("Variable '{}' not found", var_name));
            }
        }

        Ok(result)
    }
    
    /// Parse function arguments from string
    fn parse_function_args(
        &mut self,
        args_str: &str,
        variable_manager: &mut VariableManager,
        context: &mut TemplateContext,
    ) -> Result<Vec<Value>> {
        if args_str.trim().is_empty() {
            return Ok(Vec::new());
        }

        let mut args = Vec::new();
        let arg_parts: Vec<&str> = args_str.split(',').collect();

        for arg_part in arg_parts {
            let arg_trimmed = arg_part.trim();

            // Check if it's a quoted string literal
            if (arg_trimmed.starts_with('"') && arg_trimmed.ends_with('"')) ||
               (arg_trimmed.starts_with('\'') && arg_trimmed.ends_with('\'')) {
                // It's a string literal, parse it as JSON
                if let Ok(json_value) = serde_json::from_str(arg_trimmed) {
                    args.push(json_value);
                } else {
                    // Remove quotes manually if JSON parsing fails
                    let unquoted = &arg_trimmed[1..arg_trimmed.len()-1];
                    args.push(Value::String(unquoted.to_string()));
                }
            } else if let Some(var_value) = variable_manager.get_variable(arg_trimmed) {
                // It's a variable name, use its value
                args.push(var_value.clone());
            } else {
                // Try to resolve any ${} variables in the argument
                let resolved_arg = self.resolve_string_with_context(arg_trimmed, variable_manager, context)?;

                // Try to parse as JSON value
                if let Ok(json_value) = serde_json::from_str(&resolved_arg) {
                    args.push(json_value);
                } else {
                    // Treat as string literal
                    args.push(Value::String(resolved_arg));
                }
            }
        }
        
        Ok(args)
    }
    
    /// Get function registry
    pub fn function_registry(&self) -> &FunctionRegistry {
        &self.function_registry
    }
    
    /// Get mutable function registry
    pub fn function_registry_mut(&mut self) -> &mut FunctionRegistry {
        &mut self.function_registry
    }
}

impl TemplateContext {
    /// Create a new template context
    pub fn new() -> Self {
        Self {
            depth: 0,
            max_depth: 10,
            stats: TemplateStats::default(),
        }
    }
    
    /// Create a new template context with custom max depth
    pub fn with_max_depth(max_depth: usize) -> Self {
        Self {
            depth: 0,
            max_depth,
            stats: TemplateStats::default(),
        }
    }
}

impl Default for TemplateEngine {
    fn default() -> Self {
        Self::new()
    }
}

impl Default for TemplateContext {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::dsl::variables::VariableManager;

    #[test]
    fn test_template_engine_creation() {
        let engine = TemplateEngine::new();
        // Should have built-in functions registered
        assert!(!engine.function_registry.list_functions().is_empty());
    }

    #[test]
    fn test_variable_resolution() {
        let mut engine = TemplateEngine::new();
        let mut var_manager = VariableManager::new();

        var_manager.set_global_variable("name".to_string(), Value::String("John".to_string()));
        var_manager.set_global_variable("age".to_string(), Value::Number(30.into()));

        let template = "Hello ${name}, you are ${age} years old";
        let result = engine.resolve_string(template, &mut var_manager).unwrap();
        assert_eq!(result, "Hello John, you are 30 years old");
    }

    #[test]
    fn test_env_variable_resolution() {
        let mut engine = TemplateEngine::new();
        let mut var_manager = VariableManager::new();

        // Set environment variable for test
        std::env::set_var("TEST_VAR", "test_value");

        let template = "Environment: ${ENV(TEST_VAR)}";
        let result = engine.resolve_string(template, &mut var_manager).unwrap();
        assert_eq!(result, "Environment: test_value");

        // Clean up
        std::env::remove_var("TEST_VAR");
    }

    #[test]
    fn test_json_value_resolution() {
        let mut engine = TemplateEngine::new();
        let mut var_manager = VariableManager::new();

        var_manager.set_global_variable("user_id".to_string(), Value::Number(123.into()));
        var_manager.set_global_variable("active".to_string(), Value::Bool(true));

        let json_value = serde_json::json!({
            "id": "${user_id}",
            "status": "${active}",
            "nested": {
                "value": "${user_id}"
            }
        });

        let result = engine.resolve_value(&json_value, &mut var_manager).unwrap();
        assert_eq!(result["id"], Value::Number(123.into()));
        assert_eq!(result["status"], Value::Bool(true));
        assert_eq!(result["nested"]["value"], Value::Number(123.into()));
    }

    #[test]
    fn test_template_context() {
        let mut context = TemplateContext::new();
        assert_eq!(context.depth, 0);
        assert_eq!(context.max_depth, 10);
        assert_eq!(context.stats.variables_resolved, 0);
        
        context.stats.variables_resolved += 1;
        assert_eq!(context.stats.variables_resolved, 1);
    }
}
